﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using ZYSocket.share;
using System.Net.Sockets;
using CSM;

namespace CSM.Protocol
{
	/// <summary>
	/// 协议字命令的基类型，主要表明协议字的类型
	/// </summary>
	public class ProtocolBase
	{
		public ProtocolBase(ProtocolWord type) {this.Type = type;}
		public ProtocolWord Type { get; set; }
	}

	/// <summary>
	/// 用户注册类型
	/// </summary>
	public enum UserRegistType
	{
		/// <summary>
		/// 免费用户
		/// </summary>
		FreeUser,
	}

	/// <summary>
	/// 协议字列表
	/// 如果不说明，所以协议序列都是由客户端发起，服务器端被动回应
	/// 协议一般以动词开头，后面辅助说明.
	/// 最后以-Response结尾的协议字为对应协议字的回复协议字
	/// 每个协议字都会有对应的类对协议具体内容进行包装，并在Client和服务器间传递
	/// </summary>
	public enum ProtocolWord
	{
		/// <summary>
		/// 未知的命令字
		/// </summary>
		Unknown,
		/// <summary>
		/// 用户登录及Login命令的服务器回应
		/// </summary>
		Login,
		LoginResponse,
		/// <summary>
		/// 取得用户信息及LoginUser命令的服务器回应
		/// </summary>
		RequestUserInfo,
		RequestUserInfoResponse,

		/// <summary>
		/// 请求所有客户端信息用以客户端系统初始化，服务器会以类型化的信息依次下载到客户端
		/// </summary>
		RequestSystemInfo,

		/// <summary>
		/// 取得并返回系统中所有的公交公司的列表信息
		/// </summary>
		RequestCompanys,
		RequestCompanysResponse,

		/// <summary>
		/// 取得并返回指定公交公司的详细信息
		/// </summary>
		RequestCompanyDetail,
		RequestCompanyDetailResponse,

		/// <summary>
		/// 取得并返回指定公交公司所有车队列表信息
		/// </summary>
		RequestRoadTrains,
		RequestRoadTrainsResponse,

		/// <summary>
		/// 取得并返回指定公交公司所有线路列表信息
		/// </summary>
		RequestRoutes,
		RequestRoutesResponse,

		/// <summary>
		/// 取得并返回指定线路所有车辆列表信息
		/// </summary>
		RequestBuses,
		RequestBusesResponse,
	}

	/// <summary>
	/// 请求所有客户端信息，服务器会以类型化的信息依次下载到客户端
	/// </summary>
	public class RequestSystemInfo : ProtocolBase 
	{
		public RequestSystemInfo() : base(ProtocolWord.RequestSystemInfo) { }
	}

	/// <summary>
	/// 取得并返回系统中所有的公交公司的列表信息
	/// </summary>
	public class RequestCompanys : ProtocolBase
	{
		public RequestCompanys() : base(ProtocolWord.RequestCompanys) { }
	}
	public class RequestCompanysResponse : ProtocolBase
	{
		public RequestCompanysResponse() : base(ProtocolWord.RequestCompanysResponse) { }
		public long[] IDs;
		public string[] Names;
	}

	/// <summary>
	/// 取得并返回指定公交公司所有车队列表信息
	/// </summary>
	public class RequestRoadTrains : ProtocolBase
	{
		public RequestRoadTrains() : base(ProtocolWord.RequestRoadTrains) { }
		public string CompanyName;
	}
	public class RequestRoadTrainsResponse : ProtocolBase
	{
		public RequestRoadTrainsResponse() : base(ProtocolWord.RequestRoadTrainsResponse) { }
		public string CompanyName;
		public string[] RoadTrains;
	}

	/// <summary>
	/// 取得并返回指定公交公司所有线路列表信息
	/// </summary>
	public class RequestRoutes : ProtocolBase
	{
		public RequestRoutes() : base(ProtocolWord.RequestRoutes) { }
		public string CompanyName;
	}
	public class RequestRoutesResponse : ProtocolBase
	{
		public RequestRoutesResponse() : base(ProtocolWord.RequestRoutesResponse) { }
		public string CompanyName;
		public string[] Routes;
	}

	/// <summary>
	/// 取得并返回指定线路所有车辆列表信息
	/// </summary>
	public class RequestBuses : ProtocolBase
	{
		public RequestBuses() : base(ProtocolWord.RequestBuses) { }
		public string RouteName;
	}
	public class RequestBusesResponse : ProtocolBase
	{
		public RequestBusesResponse() : base(ProtocolWord.RequestBusesResponse) { }
		public string RouteName;
		public string[] Buses;
	}

	/// <summary>
	/// 用户登录
	/// </summary>
	public class Login : ProtocolBase
	{
		public Login() : base(ProtocolWord.Login) { }
		public string WorkID;
		public string Password;
	}

	/// <summary>
	/// LoginUser命令的服务器回应
	/// </summary>
	public class LoginResponse : ProtocolBase
	{
		public LoginResponse() : base(ProtocolWord.LoginResponse) { }
		public int Result;
		public string Message;
	}

	public class RequestUserInfo : ProtocolBase
	{
		public RequestUserInfo() : base(ProtocolWord.RequestUserInfo) { }
	}

	public class RequestUserInfoResponse : ProtocolBase
	{
		public RequestUserInfoResponse() : base(ProtocolWord.RequestUserInfoResponse) { }
		public long id;
		public string work_no;
		public string name;
		public DateTime birthday;
		public bool isMale;
		public string email;
		public string tel_num;
		public bool isUnregist;
		public bool isFreeze;
	}

	public static class Protocol
	{
		/// <summary>
		/// 协议报解析函数，返回解析出来的对象
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static ProtocolBase[] Parser(byte[] data, ref UserToken token)
		{
			List<ProtocolBase> ret = new List<ProtocolBase>();

			// 对协议报数据进行crc校验
			if (CheckCRC(data) == false)
			{
				throw new CRCCheckException("协议buff CRC校验错误");
			}

			// 确认UserToken已经初始化，UserToken保存有当前socket的接受buff和用户数据
			if (token == null)
			{
				token = new UserToken();
			}

			List<byte[]> datax;
			if (token.BuffManger.InsertByteArray(data, 4, out datax))
			{
				if (datax != null)
				{
					foreach (byte[] mdata in datax)
					{
						ReadBytes read = new ReadBytes(data);
						int length;
						int cmd;
						Int16 ver;
						Int16 crc;
						int rsv;

						if (read.ReadInt32(out length) && read.ReadInt32(out cmd)
							&& read.ReadInt16(out ver) && read.ReadInt16(out crc)
							&& read.ReadInt32(out rsv) && length == read.Length)
						{
							ret.Add(ReadProtocolWord(read, cmd, ver));
						}
						else
						{
							throw new InvalidDataException("不合法的协议包头信息");
						}
					}
				}
			}

			return ret.ToArray();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="cmd"></param>
		/// <param name="o"></param>
		/// <returns></returns>
		public static byte[] Encoder(ProtocolBase o)
		{
			BufferFormat buff = new BufferFormat((int)o.Type);

			buff.AddItem((Int16)ver);		//ver
			buff.AddItem((Int16)0x0000);	//crc
			buff.AddItem((int)0x00000000);	//reserve

			string proto_name = "CSMLib.Protocol." + o.Type.ToString();

			Type t = Type.GetType(proto_name);
			FieldInfo[] fields = o.GetType().GetFields();

			foreach (FieldInfo f in fields)
			{
				WriteValue(buff, o, f);
			}

			byte[] ret = buff.Finish();
			UInt16 crc = CalcCRC(ret);

			ret[11] = (byte)(crc & 0xff);
			ret[12] = (byte)((crc >> 8) & 0xff);
			return ret;
		}		/// <summary>
		/// 协议管理工具版本1.0
		/// </summary>
		static ushort ver = 0x0100;

		/// <summary>
		/// 读取协议字及相关结构信息
		/// </summary>
		/// <param name="read">通信buff</param>
		/// <param name="cmd">协议字</param>
		/// <param name="ver">版本</param>
		/// <returns>生成新的协议字结构及信息</returns>
		private static ProtocolBase ReadProtocolWord(ReadBytes read, int cmd, short ver)
		{
			ProtocolBase ret = null;

			string proto_name = "CSM.Protocol." + ((ProtocolWord)cmd).ToString();

			Type t = Type.GetType(proto_name);
			ret = (ProtocolBase)t.Assembly.CreateInstance(proto_name);

			FieldInfo[] fields = ret.GetType().GetFields();

			foreach (FieldInfo f in fields)
			{
				var rv = ReadValue(read, f);
				if (rv != null)
					f.SetValue(ret, rv);
				else
				{
					ret = null;
					break;
				}
			}

			if (ret.Type != (ProtocolWord)cmd)
			{
				throw new InvalidDataException("不合法的协议报数据");
			}
			return ret;
		}

		/// <summary>
		/// 从通信buff中读取一个字段值
		/// </summary>
		/// <param name="read">通信buff</param>
		/// <param name="f">字段信息</param>
		/// <returns>读取到的字段值</returns>
		private static object ReadValue(ReadBytes read, FieldInfo f)
		{
			object ret = null;

			switch (f.FieldType.FullName)
			{
				case "System.Boolean":
					{
						bool _value;
						if (read.ReadBoolean(out _value))
							ret = _value;
						else
							goto default;
					}
					break;
				case "System.Boolean[]":
					{
						int count;
						List<bool> tmp = new List<bool>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							bool _value;

							if (read.ReadBoolean(out _value))
								tmp.Add(_value);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.Byte":
					{
						byte _value;
						if (read.ReadByte(out _value))
							ret = _value;
						else
							goto default;
					}
					break;
				case "System.Byte[]":
					{
						byte[] _value;
						if (read.ReadByteArray(out _value))
							ret = _value;
						else
							goto default;
					}
					break;
				case "System.DateTime":
					{
						string _tmp;
						DateTime _value;

						if (read.ReadString(out _tmp))
						{
							_value = DateTime.Parse(_tmp);
							ret = _value;
						}
						else
							goto default;
					}
					break;
				case "System.DateTime[]":
					{
						int count;
						List<DateTime> tmp = new List<DateTime>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							string _value;

							if (read.ReadString(out _value))
								tmp.Add(DateTime.Parse(_value));
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.Double":
					{
						double _doubleValue;
						if (read.ReadDouble(out _doubleValue))
							ret = _doubleValue;
						else
							goto default;
					} break;
				case "System.Double[]":
					{
						int count;
						List<Double> tmp = new List<Double>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							Double _value;

							if (read.ReadDouble(out _value))
								tmp.Add(_value);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.Single":
					{
						float _floatValue;
						if (read.ReadFloat(out _floatValue))
							ret = _floatValue;
						else
							goto default;
					}
					break;
				case "System.Single[]":
					{
						int count;
						List<Single> tmp = new List<Single>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							Single _value;

							if (read.ReadFloat(out _value))
								tmp.Add(_value);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.Int32":
					{
						int _intValue;
						if (read.ReadInt32(out _intValue))
							ret = _intValue;
						else
							goto default;
					}
					break;
				case "System.Int32[]":
					{
						int count;
						List<Int32> tmp = new List<Int32>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							Int32 _value;

							if (read.ReadInt32(out _value))
								tmp.Add(_value);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.Int64":
					{
						long _longValue;
						if (read.ReadInt64(out _longValue))
							ret = _longValue;
						else
							goto default;
					}
					break;
				case "System.Int64[]":
					{
						int count;
						List<Int64> tmp = new List<Int64>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							Int64 _value;

							if (read.ReadInt64(out _value))
								tmp.Add(_value);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.Short":
					{
						short _value;
						if (read.ReadInt16(out _value))
							ret = _value;
						else
							goto default;
					}
					break;
				case "System.Short[]":
					{
						int count;
						List<short> tmp = new List<short>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							short _shortValue;

							if (read.ReadInt16(out _shortValue))
								tmp.Add(_shortValue);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.String":
					{
						string _strValue;

						if (read.ReadString(out _strValue))
							ret = _strValue;
						else
							goto default;
					}
					break;
				case "System.String[]":
					{
						int count;
						List<string> tmp = new List<string>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							string _strValue;

							if (read.ReadString(out _strValue))
								tmp.Add(_strValue);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;
				case "System.UInt64":
					long _ulongValue;
					if (read.ReadInt64(out _ulongValue))
						ret = (ulong)_ulongValue;
					else
						goto default;
					break;
				case "System.UInt64[]":
					{
						int count;
						List<UInt64> tmp = new List<UInt64>();

						if (!read.ReadInt32(out count))
							goto default;

						for (int i = 0; i < count; i++)
						{
							long _value;

							if (read.ReadInt64(out _value))
								tmp.Add((UInt64)_value);
							else
								goto default;
						}

						ret = tmp.ToArray();
					}
					break;

				// 				case "LittleBeeLib.Package.UserRegisteType":
				// 					Int32 tmp;
				// 					if (read.ReadInt32(out tmp))
				// 						ret = (UserRegisteType)tmp;
				// 					else
				// 						goto default;
				// 					break;
				default:
					throw new Exception("unknown field type, please check");
			}

			return ret;
		}

		/// <summary>
		/// 写一个协议字结构到buff
		/// </summary>
		/// <param name="cmd">协议字结构</param>
		/// <param name="o">协议字结构信息</param>
		/// <returns>形成协议字buff序列</returns>
		private static byte[] WriteProtocolWord(ProtocolWord cmd, object o)
		{
			BufferFormat buff = new BufferFormat((int)cmd);

			buff.AddItem((Int16)ver);		//ver
			buff.AddItem((Int16)0x0000);	//crc
			buff.AddItem((int)0x00000000);	//reserve

			string proto_name = "CSM.Protocol." + ((ProtocolWord)cmd).ToString();

			Type t = Type.GetType(proto_name);
			FieldInfo[] fields = o.GetType().GetFields();

			foreach (FieldInfo f in fields)
			{
				WriteValue(buff, o, f);
			}

			byte[] ret = buff.Finish();
			UInt16 crc = CalcCRC(ret);

			ret[11] = (byte)(crc & 0xff);
			ret[12] = (byte)((crc >> 8) & 0xff);
			return ret;
		}

		/// <summary>
		/// 将一个类型的值写入buff
		/// </summary>
		/// <param name="buff">要写入的buff</param>
		/// <param name="o">要被写入的类型数据</param>
		/// <param name="f">要被写入的类型信息</param>
		private static void WriteValue(BufferFormat buff, object o, FieldInfo f)
		{
			//buff.AddItem((f.FieldType)f.GetValue(o));

			switch (f.FieldType.FullName)
			{
				case "System.Boolean":
					buff.AddItem((bool)f.GetValue(o));
					break;
				case "System.Boolean[]":
					{
						bool[] tmp = (bool[])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (bool item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.Byte":
					buff.AddItem((byte)f.GetValue(o));
					break;
				case "System.Byte[]":
					buff.AddItem((byte[])f.GetValue(o));
					break;
				case "System.DateTime":
					buff.AddItem((DateTime)f.GetValue(o));
					break;
				case "System.DateTime[]":
					{
						DateTime[] tmp = (DateTime[])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (DateTime item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.Double":
					buff.AddItem((double)f.GetValue(o));
					break;
				case "System.Double[]":
					{
						Double[] tmp = (Double[])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (Double item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.Single":
					buff.AddItem((float)f.GetValue(o));
					break;
				case "System.Single[]":
					{
						Single[] tmp = (Single[])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (Single item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.Int32":
					buff.AddItem((int)f.GetValue(o));
					break;
				case "System.Int32[]":
					{
						Int32[] tmp = (Int32[])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (Int32 item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.Int64":
					buff.AddItem((long)f.GetValue(o));
					break;
				case "System.Int64[]":
					{
						Int64[] tmp = (Int64[])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (Int64 item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.Short":
					buff.AddItem((Int16)f.GetValue(o));
					break;
				case "System.Short[]":
					{
						short[] tmp = (short[])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (short item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.String":
					buff.AddItem((string)f.GetValue(o));
					break;
				case "System.String[]":
					{
						string [] tmp = (string [])f.GetValue(o);
						buff.AddItem((Int32)tmp.Length);
						foreach (string item in tmp)
						{
							buff.AddItem(item);
						}
					}
					break;
				case "System.UInt64":
					buff.AddItem((ulong)f.GetValue(o));
					break;
				// 				case "LittleBeeLib.Package.UserRegisteType":
				// 					buff.AddItem((int)f.GetValue(o));
				// 					break;
				default:
					throw new Exception("unknown field type, please check");
			}
		}

		/// <summary>
		/// 对data数据进行crc校验
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		private static bool CheckCRC(byte[] data)
		{
			UInt16 crc = (UInt16)(data[11] + (data[12] << 8));

			data[11] = 0;
			data[12] = 0;
			UInt16 crc1 = CalcCRC(data);

			return crc1 == crc;
		}

		/// <summary>
		/// 计算data的crc值
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		private static ushort CalcCRC(byte[] data)
		{
			UInt16 crc = 0;

			//计算checksum
			foreach (byte b in data)
			{
				crc = (UInt16)(crc ^ b);
			}

			return crc;
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public class UserToken
	{
		public UserToken()
		{
			BuffManger = new BuffList(4096);
		}
		public BuffList BuffManger { get; set; }
		public object Tag { get; set; }
	}
	
	/// <summary>
	/// 协议报crc校验错误异常
	/// </summary>
	public class CRCCheckException : Exception
	{
		public CRCCheckException() : base() { }
		public CRCCheckException(string msg) : base(msg) { }
	}

	/// <summary>
	/// 不认识的协议报类型异常
	/// </summary>
	public class UnknownTypeException : Exception
	{
		public UnknownTypeException() : base() { }
		public UnknownTypeException(string msg) : base(msg) { }
	}

	/// <summary>
	/// 不合法的协议报数据异常，通常是在解析协议数据过程中抛出
	/// </summary>
	public class InvalidDataException : Exception
	{
		public InvalidDataException() : base() { }
		public InvalidDataException(string msg) : base(msg) { }
	}}
